using System;
using System.Windows;
using System.Windows.Controls;

namespace WPFTemplateLib.Attached
{
	/*
	* 源码已托管：https://gitee.com/dlgcy/WPFTemplateLib
	* 版本：2024年7月21日
	*/

	/// <summary>
	/// [DLGCY] 附加属性帮助类：旋转转为位移（用于跟随旋转）。
	/// </summary>
	/// <remarks>修改自[Kimi]的回答</remarks>
	public class RotateToTranslateAttached
	{
		#region [附加属性] 旋转中心
		public static Point GetRotationCenter(DependencyObject obj)
		{
			return (Point)obj.GetValue(RotationCenterProperty);
		}
		/// <summary>
		/// 旋转中心
		/// </summary>
		public static void SetRotationCenter(DependencyObject obj, Point value)
		{
			obj.SetValue(RotationCenterProperty, value);
		}
		/// <summary>
		/// [附加属性] 旋转中心
		/// </summary>
		public static readonly DependencyProperty RotationCenterProperty =
			DependencyProperty.RegisterAttached("RotationCenter", typeof(Point), typeof(RotateToTranslateAttached), new PropertyMetadata(new Point(), OnRotationCenterChanged));
		/// <summary>
		/// 旋转中心改变
		/// </summary>
		private static void OnRotationCenterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			if(d is FrameworkElement element)
			{
				Point renderTransformOrigin = element.RenderTransformOrigin;
				double width = element.Width;
				double height = element.Height;
				double left = Canvas.GetLeft(element);
				double top = Canvas.GetTop(element);
				double transformX = left + width * renderTransformOrigin.X;
				double transformY = top + height * renderTransformOrigin.Y;
				double gapX = transformX - left;
				double gapY = transformY - top;
				SetGapPoint(d, new Point(gapX, gapY));

				var rotationCenter = GetRotationCenter(d);
				var rotationPointRelative = new Point(transformX - rotationCenter.X, transformY - rotationCenter.Y);
				SetRotationPointRelative(d, rotationPointRelative);
			}
		}
		#endregion

		#region [附加属性] 旋转点相对位置（私有）
		private static Point GetRotationPointRelative(DependencyObject obj)
		{
			return (Point)obj.GetValue(RotationPointRelativeProperty);
		}
		/// <summary>
		/// 旋转点相对位置（使用跟随元素的坐标减去旋转元素中心点的坐标）
		/// </summary>
		private static void SetRotationPointRelative(DependencyObject obj, Point value)
		{
			obj.SetValue(RotationPointRelativeProperty, value);
		}
		/// <summary>
		/// [附加属性] 旋转点相对位置
		/// </summary>
		public static readonly DependencyProperty RotationPointRelativeProperty =
			DependencyProperty.RegisterAttached("RotationPointRelative", typeof(Point), typeof(RotateToTranslateAttached), new PropertyMetadata(new Point()));
		#endregion

		#region [附加属性] 变化角度
		public static double GetChangedAngle(DependencyObject obj)
		{
			return (double)obj.GetValue(ChangedAngleProperty);
		}
		/// <summary>
		/// 变化角度
		/// </summary>
		public static void SetChangedAngle(DependencyObject obj, double value)
		{
			obj.SetValue(ChangedAngleProperty, value);
		}
		/// <summary>
		/// [附加属性] 变化角度
		/// </summary>
		public static readonly DependencyProperty ChangedAngleProperty =
			DependencyProperty.RegisterAttached("ChangedAngle", typeof(double), typeof(RotateToTranslateAttached), new PropertyMetadata(0d, ChangedAngleChangedCallback));
		/// <summary>
		/// 变化角度变化
		/// </summary>
		private static void ChangedAngleChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			if(d is FrameworkElement element)
			{
				UpdateFollowPointPosition(element);
			}
		}
		#endregion

		#region [附加属性] 距离（私有）
		private static Point GetGapPoint(DependencyObject obj)
		{
			return (Point)obj.GetValue(GapPointProperty);
		}
		/// <summary>
		/// 距离
		/// </summary>
		private static void SetGapPoint(DependencyObject obj, Point value)
		{
			obj.SetValue(GapPointProperty, value);
		}
		/// <summary>
		/// [附加属性] 距离
		/// </summary>
		public static readonly DependencyProperty GapPointProperty =
			DependencyProperty.RegisterAttached("GapPoint", typeof(Point), typeof(RotateToTranslateAttached), new PropertyMetadata(new Point()));
		#endregion

		/// <summary>
		/// 更新位置
		/// </summary>
		private static void UpdateFollowPointPosition(DependencyObject d)
		{
			var element = d as FrameworkElement;
			if(element == null)
				return;

			var angle = GetChangedAngle(d);
			var rotationCenter = GetRotationCenter(d);
			var rotationPointRelative = GetRotationPointRelative(d);
			var gapPoint = GetGapPoint(d);

			var newLeft = rotationCenter.X + rotationPointRelative.X * Math.Cos(angle * Math.PI / 180) - rotationPointRelative.Y * Math.Sin(angle * Math.PI / 180);
			var newTop = rotationCenter.Y + rotationPointRelative.Y * Math.Cos(angle * Math.PI / 180) + rotationPointRelative.X * Math.Sin(angle * Math.PI / 180);

			newLeft = Math.Round(newLeft - gapPoint.X, 1);
			newTop = Math.Round(newTop - gapPoint.Y, 1);
			Canvas.SetLeft(element, newLeft);
			Canvas.SetTop(element, newTop);
		}
	}
}
